package com.sad.jetpack.v1.datamodel.api;


import com.sad.jetpack.v1.datamodel.api.utils.CollectionTraverseUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public class InternalDataModelProduceFuture<RP> implements IDataModelProduceFuture<RP>,IDataModelProduceFuture.Creator<RP>{
    private int currState= NOT_STARTED;
    private IDataModelResponse<RP> response;
    private Throwable exception;
    private IDataModelRequest request;
    private List<IDataModelProduceFutureObserver<RP>> observers=new ArrayList<>();
    private AtomicBoolean cancelled=new AtomicBoolean(false);
    private IDataModelProducer<RP> producer;
    public InternalDataModelProduceFuture(IDataModelRequest request,IDataModelProducer<RP> producer){
        this.request=request;
        this.producer=producer;
    }

    public InternalDataModelProduceFuture<RP> updateRequest(IDataModelRequest request) {
        this.request = request;
        return this;
    }

    @Override
    public IDataModelProduceFuture<RP> create() {
        return this;
    }

    public InternalDataModelProduceFuture<RP> updateBindingProducer(IDataModelProducer<RP> producer) {
        this.producer = producer;
        return this;
    }

    @Override
    public IDataModelRequest getRequest() {
        return request;
    }

    @Override
    public <T extends Throwable> T getException() {
        return (T) exception;
    }

    @Override
    public void cancel() {
        this.cancelled.set(true);
        this.currState=CANCELLED;
        CollectionTraverseUtils.traverseList(observers, new CollectionTraverseUtils.ITraverseAction<Integer, IDataModelProduceFutureObserver<RP>>() {
            @Override
            public void onTraversed(Integer integer, IDataModelProduceFutureObserver<RP> rpiDataModelProduceFutureObserver) throws Exception {
                rpiDataModelProduceFutureObserver.onDataRequestCancelled(request);
            }
        });
    }

    @Override
    public IDataModelResponse<RP> getResponse() {
        return response;
    }

    @Override
    public int getCurrState() {
        return currState;
    }

    @Override
    public void addObserver(boolean sticky,IDataModelProduceFutureObserver observer) {

        if (sticky){
            if (NOT_STARTED==currState){
                observer.onDataRequestNotStarted(request);
            }
            else if (RUNNING==currState){
                observer.onDataRequestProcessing(request);
            }
            else if (EXCEPTION==currState){
                observer.onDataRequestException(request,exception);
            }
            else if (DONE==currState){
                observer.onDataResponseChanged(response);
            }
            else if (CANCELLED==currState){
                observer.onDataRequestCancelled(request);
            }
        }
        observers.add(observer);
    }

    @Override
    public void removeObserver(IDataModelProduceFutureObserver observer,boolean resetStateWhenNoObserver) {
        observers.remove(observer);
        if (resetStateWhenNoObserver && !hasObservers()){
            resetFutureState();
        }
    }

    @Override
    public void removeObserver(String id,boolean resetStateWhenNoObserver) {
        List<IDataModelProduceFutureObserver<RP>> observersClone = new ArrayList<>();
        for
        (IDataModelProduceFutureObserver<RP> observer : observers) {
            if (!observer.observerId().equals(id)) {
                observersClone.add(observer);
            }
        }
        this.observers=observersClone;
        if (resetStateWhenNoObserver && !hasObservers()){
            resetFutureState();
        }
    }
    @Override
    public void clearObservers(boolean resetStateWhenNoObserver) {
        observers.clear();
        if (resetStateWhenNoObserver){
            resetFutureState();
        }

    }

    @Override
    public void resetFutureState() {
        this.response=null;
        this.currState=NOT_STARTED;
    }

    @Override
    public IDataModelProduceFuture<RP> unregisterSelf() {
        return DataModelProduceFutureProvider.unregister(request.tag());
    }

    @Override
    public void registerSelf() {
        DataModelProduceFutureProvider.register(this);
    }

    @Override
    public IDataModelProducer<RP> newRequestToExecute(IDataModelRequest request) {
        if (producer!=null){
            updateRequest(request);
            producer.newRequest(request)
                    .execute()
            ;
        }
        return producer;
    }

    @Override
    public void reRequestToExecute() {
        if (producer!=null){
            producer.newRequest(request)
                    .execute()
            ;
        }
    }

    @Override
    public boolean hasObservers() {
        return observers!=null && observers.size()>0;
    }

    @Override
    public Creator<RP> toCreator() {
        return this;
    }

    @Override
    public void notifyObserversException(IDataModelRequest request, Throwable e) {
        updateRequest(request);
        this.exception=e;
        this.currState=EXCEPTION;
        CollectionTraverseUtils.traverseList(observers, new CollectionTraverseUtils.ITraverseAction<Integer, IDataModelProduceFutureObserver<RP>>() {
            @Override
            public void onTraversed(Integer integer, IDataModelProduceFutureObserver iDataModelProduceFutureObserver) throws Exception {
                iDataModelProduceFutureObserver.onDataRequestException(request,e);
            }
        });
    }

    @Override
    public void notifyObserversResponseChanged(IDataModelResponse<RP> response) {
        this.response=response;
        updateRequest(request);
        this.currState=DONE;
        CollectionTraverseUtils.traverseList(observers, new CollectionTraverseUtils.ITraverseAction<Integer, IDataModelProduceFutureObserver<RP>>() {
            @Override
            public void onTraversed(Integer integer, IDataModelProduceFutureObserver iDataModelProduceFutureObserver) throws Exception {
                iDataModelProduceFutureObserver.onDataResponseChanged(response);
            }
        });
    }

    @Override
    public void notifyObserversNotStarted(IDataModelRequest request) {
        updateRequest(request);
        this.currState=NOT_STARTED;
        CollectionTraverseUtils.traverseList(observers, new CollectionTraverseUtils.ITraverseAction<Integer, IDataModelProduceFutureObserver<RP>>() {
            @Override
            public void onTraversed(Integer integer, IDataModelProduceFutureObserver iDataModelProduceFutureObserver) throws Exception {
                iDataModelProduceFutureObserver.onDataRequestNotStarted(request);
            }
        });
    }

    @Override
    public void notifyObserversProcessing(IDataModelRequest request) {
        updateRequest(request);
        this.currState=RUNNING;
        CollectionTraverseUtils.traverseList(observers, new CollectionTraverseUtils.ITraverseAction<Integer, IDataModelProduceFutureObserver<RP>>() {
            @Override
            public void onTraversed(Integer integer, IDataModelProduceFutureObserver iDataModelProduceFutureObserver) throws Exception {
                iDataModelProduceFutureObserver.onDataRequestProcessing(request);
            }
        });
    }

    @Override
    public boolean cancelled() {
        return this.cancelled.get();
    }
}
